package org.springrain.selenium.service.impl;

import java.io.File;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.springframework.stereotype.Service;
import org.springrain.frame.common.SessionUser;
import org.springrain.frame.entity.IBaseEntity;
import org.springrain.frame.util.Finder;
import org.springrain.frame.util.GlobalStatic;
import org.springrain.frame.util.JsonUtils;
import org.springrain.frame.util.Page;
import org.springrain.frame.util.SecUtils;
import org.springrain.selenium.dto.ConditionDto;
import org.springrain.selenium.entity.Htmlcaselog;
import org.springrain.selenium.entity.Htmloperation;
import org.springrain.selenium.entity.Htmloperationcollection;
import org.springrain.selenium.entity.Htmlparameter;
import org.springrain.selenium.service.IHtmlSeleniumService;
import org.springrain.selenium.util.SeleniumUtils;
import org.springrain.system.service.BaseSpringrainServiceImpl;
import org.springrain.system.service.ITableindexService;

/**
 * TODO 在此加入类描述
 * 
 * @copyright {@link weicms.net}
 * @author springrain<Auto generate>
 * @version 2018-05-21 20:17:39
 * @see org.springrain.selenium.service.impl.Htmloperation
 */
@Service("htmlSeleniumService")
public class HtmlSeleniumServiceImpl extends BaseSpringrainServiceImpl implements IHtmlSeleniumService {
	@Resource
	private ITableindexService tableindexService;

	@Override
	public String save(Object entity) throws Exception {
		Htmloperation htmloperation = (Htmloperation) entity;
		return super.save(htmloperation).toString();
	}

	@Override
	public String saveorupdate(Object entity) throws Exception {
		Htmloperation htmloperation = (Htmloperation) entity;
		return super.saveorupdate(htmloperation).toString();
	}

	@Override
	public Integer update(IBaseEntity entity) throws Exception {
		Htmloperation htmloperation = (Htmloperation) entity;
		return super.update(htmloperation);
	}

	@Override
	public Htmloperation findHtmloperationById(String id) throws Exception {
		return super.findById(id, Htmloperation.class);
	}

	/**
	 * 列表查询,每个service都会重载,要把sql语句封装到service中,Finder只是最后的方案
	 * 
	 * @param finder
	 * @param page
	 * @param clazz
	 * @param o
	 * @return
	 * @throws Exception
	 */
	@Override
	public <T> List<T> findListDataByFinder(Finder finder, Page page, Class<T> clazz, Object o) throws Exception {
		return super.findListDataByFinder(finder, page, clazz, o);
	}

	/**
	 * 根据查询列表的宏,导出Excel
	 * 
	 * @param finder 为空则只查询 clazz表
	 * @param ftlurl 类表的模版宏
	 * @param page   分页对象
	 * @param clazz  要查询的对象
	 * @param o      querybean
	 * @return
	 * @throws Exception
	 */
	@Override
	public <T> File findDataExportExcel(Finder finder, String ftlurl, Page page, Class<T> clazz, Object o)
			throws Exception {
		return super.findDataExportExcel(finder, ftlurl, page, clazz, o);
	}

	/**
	 * 批量执行动作
	 * 
	 * @param driver
	 * @param operations
	 * @param businessId 入口的节点Id
	 * @param execId     执行动作的Id
	 * @return
	 * @throws Exception
	 */
	public String updateExecOperation(WebDriver driver, List<Htmloperation> operations, String businessId,
			String execId) throws Exception {
		if (CollectionUtils.isEmpty(operations)) {
			return null;
		}

		if (driver == null) {
			return null;
		}

		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < operations.size(); i++) {
			Htmloperation operation = operations.get(i);
			String result = updateExecOperation(driver, operation, businessId, i, execId);
			if (StringUtils.isNotEmpty(result)) {
				sb.append(result).append(",");
			}
		}

		if (StringUtils.isBlank(sb)) {
			return null;
		}

		return sb.toString();
	}

	/**
	 * 执行一个操作
	 * 
	 * @param driver
	 * @param operation
	 * @param businessId 入口的节点Id
	 * @param index      这次所有动作列表中的index
	 * @param execId     执行动作的Id
	 * @return
	 * @throws Exception
	 */
	public String updateExecOperation(WebDriver driver, Htmloperation operation, String businessId, Integer index,
			String execId) throws Exception {

		// 不能执行
		if (!execCondition(operation.getConditionStr(), driver)) {
			return null;
		}

		// 错误时截屏图片
		String screenshot = null;
		boolean savcaselog = false;// 是否保存执行记录
		Integer pass = 0;// 默认验证失败
		// 真实获取的结果
		String realValue = null;

		Integer operationType = operation.getOperationType();
		String operationXpath = operation.getOperationXpath();
		String operationValue = operation.getOperationValue();

		if (1 - operation.getParameter() == 0) {// 用户自定义参数
			Finder f = Finder.getSelectFinder(Htmlparameter.class, "operationValue")
					.append(" WHERE execId=:execId and operationId=:operationId and sortno=:sortno ");
			f.setParam("execId", execId).setParam("operationId", operation.getId()).setParam("sortno", index);
			String string = super.queryForObject(f, String.class);
			if (StringUtils.isNotBlank(string)) {
				operationValue = string;
			}
		}

		Integer validateProgress = operation.getValidateProgress();

		try {

			if (operationType - 1 == 0) {// 打开
				driver.get(operationXpath);

			} else if (operationType - 2 == 0) {// 赋值
				WebElement webElement = driver.findElement(By.xpath(operationXpath));
				webElement.clear();
				webElement.sendKeys(operationValue); // 设置字段值

			} else if (operationType - 3 == 0) {// 单击
				driver.findElement(By.xpath(operationXpath)).click();

			} else if (operationType - 4 == 0) {// 双击
				Actions action = new Actions(driver).doubleClick(driver.findElement(By.xpath(operationXpath)));
				action.build().perform();
			} else if (operationType - 5 == 0) {// switchTo

				if ("iframe".equals(operationValue)) {
					driver.switchTo().frame(driver.findElement(By.xpath(operationXpath)));
				} else if ("parentFrame".equals(operationValue)) {
					driver.switchTo().parentFrame();
				} else if ("default".equals(operationValue)) {
					driver.switchTo().defaultContent();
				} else {
					driver.switchTo().defaultContent();
				}

			} else if (operationType - 6 == 0) {// document
				if ("title".equals(operationXpath)) {
					realValue = driver.getTitle();
				}

			} else if (operationType - 7 == 0) {// 右键
				Actions action = null;
				if (StringUtils.isNotBlank(operationXpath)) {
					action = new Actions(driver).contextClick(driver.findElement(By.xpath(operationXpath)));
				} else {
					action = new Actions(driver).contextClick();
				}
				action.build().perform();
			} else if (operationType - 8 == 0) {// 悬浮
				Actions action = null;
				if (StringUtils.isNotBlank(operationXpath)) {
					action = new Actions(driver).clickAndHold(driver.findElement(By.xpath(operationXpath)));
				} else {
					action = new Actions(driver).clickAndHold();
				}
				action.build().perform();
			} else if (operationType - 9 == 0) {// 拖拽
				if (StringUtils.isNotBlank(operationXpath) && StringUtils.isNotBlank(operationValue)) {
					Actions action = new Actions(driver).dragAndDrop(driver.findElement(By.xpath(operationValue)),
							driver.findElement(By.xpath(operationXpath)));
					action.build().perform();
				}

			} else if (operationType - 11 == 0) {// JS指令
				JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
				jsExecutor.executeScript(operationValue);
			}

			if (validateProgress - 3 == 0) {// 验证
				savcaselog = true;
				if (realValue == null) {
					realValue = driver.findElement(By.xpath(operationXpath)).getText();
				}
				if (ValidateXapthValue(driver, operationXpath, operation.getValidateMethod(), operationValue,
						realValue)) {// 如果验证结果正确.
					pass = 1;
				}

			}
		} catch (Exception e) {
			savcaselog = true;
			pass = 0;
			realValue = e.getMessage();
			logger.error(e.getMessage(), e);
		}

		if (savcaselog) {// 保存

			try {
				// 指定了OutputType.FILE做为参数传递给getScreenshotAs()方法，其含义是将截取的屏幕以文件形式返回。
				File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
				String destDirPath = GlobalStatic.rootDir + "/upload/selenium/" + execId;
				screenshot = "/upload/selenium/" + execId + "/" + SecUtils.getUUID() + ".png";
				File destDir = new File(destDirPath);
				if (!destDir.exists()) {
					destDir.mkdirs();
				}

				File dest = new File(GlobalStatic.rootDir + screenshot);
				srcFile.renameTo(dest);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}

			Htmlcaselog caselog = new Htmlcaselog();
			caselog.setExecId(execId);
			caselog.setScreenshot(screenshot);

			caselog.setBusinessId(businessId);
			caselog.setBusinessName(findOperationCollectionNameById(businessId));

			caselog.setExecBusinessId(operation.getBusinessId());
			caselog.setExecName(findOperationCollectionNameById(operation.getBusinessId()));

			caselog.setOperationId(operation.getId());
			caselog.setOperationName(operation.getName());

			caselog.setRealResult(realValue);
			caselog.setCaseValue(operationValue);

			caselog.setPass(pass);
			caselog.setBrowser(1);

			caselog.setUserId(SessionUser.getUserId());
			caselog.setCreateTime(new Date());
			caselog.setSortno(new BigDecimal(index));

			super.save(caselog);
		}

		Thread.sleep(operation.getWaitTime());

		return null;
	}

	/**
	 * 递归执行条件,判断条件结果
	 * 
	 * @param list
	 * @return
	 */
	private Boolean recursionCondition(List<ConditionDto> list, WebDriver driver) {
		if (CollectionUtils.isEmpty(list)) {// 没有条件 默认true
			return Boolean.TRUE;
		}
		Boolean flag = Boolean.FALSE; // 结果是 false;
		for (ConditionDto dto : list) {
			// 判断条件 0是or,1是and 可以为空
			Integer andor = dto.getAndor();

			// 默认本次循环的结果是false
			boolean testok = false;
			if (CollectionUtils.isEmpty(dto.getListCondition())) {// 没有子条件,判断xpath和校验结果
				testok = ValidateXapthValue(driver, dto.getOperationXpath(), dto.getValidateMethod(),
						dto.getOperationValue(), null);
			} else {
				// 有子条件,递归
				testok = recursionCondition(dto.getListCondition(), driver);
			}

			if (andor == null) {// 如果没有指定andor,就是条件的第一个,只有循环的第一个才会是null,赋值给全局的值
				flag = testok;
			} else if (0 - andor == 0) {// or
				flag = flag || testok;
			} else if (1 - andor == 0) {// and
				flag = flag && testok;
			}
		}

		return flag;
	}

	/**
	 * 验证xpath的值是否正确
	 * 
	 * @param driver
	 * @param operationXpath
	 * @param validateMethod
	 * @param operationValue
	 * @param realValue
	 * @return
	 */
	private boolean ValidateXapthValue(WebDriver driver, String operationXpath, Integer validateMethod,
			String operationValue, String realValue) {
		if (realValue == null) {
			realValue = driver.findElement(By.xpath(operationXpath)).getText();
		}
		if ((validateMethod - 1 == 0) && realValue.equals(operationValue)) {// 等于的验证方法
			return true;
		} else if ((validateMethod - 6 == 0) && (realValue.contains(operationValue))) {// 包含的验证方法
			return true;
		} else if ((validateMethod - 7 == 0) && (!realValue.equals(operationValue))) {// 等于的验证方法
			return true;
		} else if ((validateMethod - 8 == 0) && (realValue.startsWith(operationValue))) {// 以开始
			return true;
		} else if ((validateMethod - 9 == 0) && (realValue.endsWith(operationValue))) {// 以结束
			return true;
		} else if ((validateMethod - 2 == 0) && (compareToIgnoreCase(realValue, operationValue) > 0)) {// 大于
			return true;
		} else if ((validateMethod - 3 == 0) && (compareToIgnoreCase(realValue, operationValue) >= 0)) {// 大于等于
			return true;
		} else if ((validateMethod - 4 == 0) && (compareToIgnoreCase(realValue, operationValue) < 0)) {// 小于
			return true;
		} else if ((validateMethod - 5 == 0) && (compareToIgnoreCase(realValue, operationValue) <= 0)) {// 小于等于
			return true;
		}

		return false;
	}

	@Override
	public List<Htmloperation> findListBybusinessId(String businessId, Page page) throws Exception {

		Finder finder = Finder.getSelectFinder(Htmloperation.class)
				.append(" WHERE businessId=:businessId   order by sortno asc ").setParam("businessId", businessId);
		// ==执行分页查询
		List<Htmloperation> list = super.queryForList(finder, Htmloperation.class, page);

		return list;
	}

	@Override
	public String saveHtmloperationcollection(Htmloperationcollection htmloperationcollection) throws Exception {

		// String id=SecUtils.getUUID();
		String id = null;
		if (StringUtils.isNotBlank(htmloperationcollection.getId())) {
			id = htmloperationcollection.getId();
		} else {
			id = tableindexService.updateNewId(Htmloperationcollection.class);
		}
		htmloperationcollection.setId(id);

		String comcode = findHtmloperationcollectionNewComcode(id, htmloperationcollection.getPid());

		htmloperationcollection.setComcode(comcode);

		super.save(htmloperationcollection);

		return id;

	}

	@Override
	public Integer updateHtmloperationcollection(Htmloperationcollection htmloperationcollection) throws Exception {
		String pid = htmloperationcollection.getPid();
		htmloperationcollection.setComcode(null);

		String id = htmloperationcollection.getId();
		if (StringUtils.isEmpty(id)) {
			return null;
		}

		Finder f_old_c = Finder.getSelectFinder(Htmloperationcollection.class, "comcode").append(" WHERE id=:id ")
				.setParam("id", id);

		String old_c = super.queryForObject(f_old_c, String.class);

		String new_c = findHtmloperationcollectionNewComcode(id, pid);

		if (new_c.equalsIgnoreCase(old_c)) {// 编码没有改变
			return super.update(htmloperationcollection, true);

		}
		htmloperationcollection.setComcode(new_c);
		Integer update = super.update(htmloperationcollection, true);
		// 级联更新
		Finder f_s_list = Finder.getSelectFinder(Htmloperationcollection.class, "id,comcode")
				.append(" WHERE comcode like :comcode and id<>:id ").setParam("comcode", old_c + "%")
				.setParam("id", id);
		List<Htmloperationcollection> list = super.queryForList(f_s_list, Htmloperationcollection.class);
		if (CollectionUtils.isEmpty(list)) {
			return update;
		}

		for (Htmloperationcollection opc : list) {
			String _id = opc.getId();
			String _c = findHtmloperationcollectionNewComcode(_id, id);
			opc.setComcode(_c);
			opc.setPid(id);

		}

		super.update(list, true);

		return update;
	}

	public String findHtmloperationcollectionNewComcode(String id, String pid) throws Exception {

		if (StringUtils.isEmpty(id)) {
			return null;
		}

		String comcode = null;
		Finder f_p_c = Finder.getSelectFinder(Htmloperationcollection.class, "comcode").append(" WHERE id=:pid ")
				.setParam("pid", pid);
		String p_c = super.queryForObject(f_p_c, String.class);
		// 如果没有上级部门
		if (StringUtils.isEmpty(p_c)) {
			p_c = ",";
		}

		comcode = p_c + id + ",";

		return comcode;
	}

	@Override
	public String saveHtmloperation(Htmloperation htmloperation) throws Exception {

		String id = tableindexService.updateNewId(Htmloperation.class);
		htmloperation.setId(id);
		return (String) super.save(htmloperation);
	}

	@Override
	public Integer updateHtmloperation(Htmloperation htmloperation) throws Exception {
		return super.update(htmloperation);
	}

	@Override
	public String updateExecOperationBybusinessId(String businessId, List<Htmloperation> list) throws Exception {

		if (list == null) {
			return "容器为空";
		}

		if (StringUtils.isBlank(businessId)) {
			return "业务Id为空";
		}

		Htmloperationcollection htmloperationcollection = findHtmloperationcollectionById(businessId);

		if (htmloperationcollection == null) {
			return "业务Id错误";
		}

		String comcode = htmloperationcollection.getComcode();
		if (StringUtils.isBlank(comcode)) {
			comcode = "," + htmloperationcollection.getId() + ",";
		}

		// 获取前面节点的id
		String[] bids = comcode.split(",");

		for (String bid : bids) {
			if (StringUtils.isBlank(bid)) {
				continue;
			}

			recursionListOperation(bid, null, list, false);// 添加父级节点和本节点 的操作,不包含子孙节点
		}

		// 获取 执行节点下的子节点,不包含本节点
		List<Htmloperationcollection> findChildListByPid = findChildListByPid(businessId);

		if (CollectionUtils.isNotEmpty(findChildListByPid)) {// 有下级节点
			for (Htmloperationcollection h : findChildListByPid) {// 递归循环每个下级节点.
				recursionListOperation(h.getId(), null, list, true);
			}
		}

		return null;
	}

	@Override
	public String updateExecOperationList(List<Htmloperation> list, String businessId, String execId) throws Exception {
		if (CollectionUtils.isEmpty(list)) {
			return "没有可以执行的操作";
		}
		// 开启 selenium
		WebDriver driver = SeleniumUtils.initDriver();
		String errorMsg = updateExecOperation(driver, list, businessId, execId);
		driver.quit();
		Thread.sleep(1000);
		return errorMsg;
	}

	@Override
	public Htmloperationcollection findHtmloperationcollectionById(String id) throws Exception {
		return super.findById(id, Htmloperationcollection.class);
	}

	/**
	 * 递归添加层级里的动作,添加层级直接关联的动作和子孙节点关联的动作.
	 * 
	 * @param operation
	 * @param list
	 * @return
	 * @throws Exception
	 */
	private void recursionListOperation(String businessId, Integer validateProgress, List<Htmloperation> list,
			Boolean leaf) throws Exception {

		if (StringUtils.isBlank(businessId)) {
			return;
		}

		// 获取前面节点验证前的操作
		Finder f = Finder.getSelectFinder(Htmloperation.class).append(" WHERE businessId=:businessId  ")
				.setParam("businessId", businessId);

		if (validateProgress != null) {
			f.append("  and validateProgress=:validateProgress ").setParam("validateProgress", validateProgress);
		}

		f.append(" order by sortno asc ");

		List<Htmloperation> findListBybusinessId = super.queryForList(f, Htmloperation.class);
		if (CollectionUtils.isNotEmpty(findListBybusinessId)) {// 如果由直接的动作就进行循环
			// 循环调用前面节点需要的操作
			for (Htmloperation operation : findListBybusinessId) {
				if (0 - operation.getOperationType() != 0) {// 普通节点
					list.add(operation);
					continue;
				}

				// 如果是个引用的是个集合,执行子孙节点
				recursionListOperation(operation.getOperationXpath(), null, list, true);
			}
		}

		if (!leaf) {// 不执行子孙节点
			return;
		}

		// 获取 执行节点的字节点,不包含执行节点
		List<Htmloperationcollection> findChildListByPid = findChildListByPid(businessId);
		if (CollectionUtils.isNotEmpty(findChildListByPid)) {
			for (Htmloperationcollection h : findChildListByPid) {// 递归循环每个下级节点.
				recursionListOperation(h.getId(), null, list, leaf);
			}
		}

	}

	/**
	 * 根据 businessId ,like下面所有的 Htmloperationcollection,不包含自己
	 * 
	 * @param businessId
	 * @return
	 * @throws Exception
	 */

	@Deprecated
	private List<Htmloperationcollection> findLikeList(String businessId) throws Exception {
		if (StringUtils.isEmpty(businessId)) {
			return null;
		}

		Finder f = Finder.getSelectFinder(Htmloperationcollection.class, "comcode").append(" WHERE id=:id ")
				.setParam("id", businessId);
		String comcode = super.queryForObject(f, String.class);

		if (StringUtils.isEmpty(comcode)) {
			return null;
		}

		Finder finder = Finder.getSelectFinder(Htmloperationcollection.class)
				.append(" WHERE comcode like :comcode and id<>:id  order by comcode asc,sortno asc ")
				.setParam("comcode", comcode + "%").setParam("id", businessId);
		return super.queryForList(finder, Htmloperationcollection.class);

	}

	/**
	 * 根据pid 查询 子节点
	 * 
	 * @param pid
	 * @return
	 * @throws Exception
	 */
	private List<Htmloperationcollection> findChildListByPid(String pid) throws Exception {
		if (StringUtils.isEmpty(pid)) {
			return null;
		}

		Finder f = Finder.getSelectFinder(Htmloperationcollection.class, "id").append(" WHERE pid=:pid ")
				.setParam("pid", pid);
		return super.queryForList(f, Htmloperationcollection.class);

	}

	/**
	 * 判断条件的表达式,返回Boolean结果
	 * 
	 * @param conditionStr
	 * @return
	 */
	@Override
	public Boolean execCondition(String conditionStr, WebDriver driver) {

		if (StringUtils.isBlank(conditionStr)) {
			return Boolean.TRUE;
		}

		List<ConditionDto> list = JsonUtils.readValues(conditionStr, ConditionDto.class);
		if (CollectionUtils.isEmpty(list)) {
			return Boolean.TRUE;
		}

		return recursionCondition(list, driver);
	}

	@Override
	public String findOperationNameById(String id) throws Exception {

		if (StringUtils.isBlank(id)) {
			return null;
		}

		Finder finder = Finder.getSelectFinder(Htmloperation.class, "name").append(" WHERE id=:id ").setParam("id", id);
		return super.queryForObject(finder, String.class);
	}

	@Override
	public String findOperationCollectionNameById(String id) throws Exception {

		if (StringUtils.isBlank(id)) {
			return null;
		}

		Finder finder = Finder.getSelectFinder(Htmloperationcollection.class, "name").append(" WHERE id=:id ")
				.setParam("id", id);
		return super.queryForObject(finder, String.class);
	}

	/**
	 * 比较字符串大小,如果是数字,则按照数字对比,如果是字符串,就按照字符串对比 str1 等于 str2 返回0,str1小于str2
	 * 返回负数,str1大于str2返回正数
	 * 
	 * @param str1
	 * @param str2
	 * @return
	 */
	private int compareToIgnoreCase(String str1, String str2) {
		if (str1 == null && str2 == null) {
			return 0;
		}

		if (str1 == null && str2 != null) {
			return -1;
		}
		if (str1 != null && str2 == null) {
			return 1;
		}

		if (NumberUtils.isCreatable(str1) && NumberUtils.isCreatable(str2)) {// 如果两个都是数字
			BigDecimal d1 = new BigDecimal(str1);
			BigDecimal d2 = new BigDecimal(str2);
			BigDecimal subtract = d1.subtract(d2);
			return subtract.compareTo(BigDecimal.ZERO);
		}

		return str1.compareToIgnoreCase(str2);

	}

}
